import uuid
from unittest.mock import patch
from datetime import datetime, timedelta
from rest_api.rest_api_server.tests.unittests.test_api_base import TestApiBase
from rest_api.rest_api_server.exceptions import Err
from rest_api.rest_api_server.models.db_factory import DBFactory, DBType
from rest_api.rest_api_server.models.db_base import BaseDB
from rest_api.rest_api_server.models.models import Employee, OrganizationLimitHit
from sqlalchemy import and_
from tools.optscale_exceptions.http_exc import OptHTTPError
import tools.optscale_time as opttime


class TestEmployeeApi(TestApiBase):

    def setUp(self, version='v2'):
        super().setUp(version)
        _, self.org = self.client.organization_create(
            {'name': "organization"})
        self.org_id = self.org['id']
        self.user_id = self.gen_id()
        self.user_id2 = self.gen_id()
        self._mock_auth_user(self.user_id)
        self.p_get_user_info.return_value = {
            'display_name': 'User', 'id': self.user_id,
            'email': 'user@mail.com', 'is_password_autogenerated': False}
        self.valid_employee = {
            'name': 'John Smith',
            'auth_user_id': self.user_id,
        }
        self.valid_employee2 = {
            'name': 'John Smith II',
            'auth_user_id': self.user_id2,
        }

    def _mock_auth(self):
        patch('rest_api.rest_api_server.controllers.assignment.AssignmentController.'
              '_authorize_action_for_pool', return_value=True).start()
        patch('rest_api.rest_api_server.controllers.pool.PoolController.'
              '_authorize_action_for_user', return_value=True).start()
        patch('optscale_client.auth_client.client_v2.Client.assignment_delete',
              return_value=(204, None)).start()

    def prepare_pools_hierarchy(self):
        # pools:
        #     root pool:
        #         - p1: (p11, p12)
        #         - p2: (p21, p22)
        root_pool = self.org['pool_id']
        names = ['p1', 'p2']
        pools = {}
        for name in names:
            code, resp = self.client.pool_create(
                self.org_id, {'name': name, 'parent_id': root_pool})
            self.assertEqual(code, 201)
            pools[name] = resp['id']

        names = ['p11', 'p12']
        for name in names:
            code, resp = self.client.pool_create(
                self.org_id, {'name': name, 'parent_id': pools['p1']})
            self.assertEqual(code, 201)
            pools[name] = resp['id']

        names = ['p21', 'p22']
        for name in names:
            code, resp = self.client.pool_create(
                self.org_id, {'name': name, 'parent_id': pools['p2']})
            self.assertEqual(code, 201)
            pools[name] = resp['id']
        return pools

    def test_create_employee(self):
        code, employee = self.client.employee_create(self.org_id,
                                                     self.valid_employee)
        self.assertEqual(code, 201)
        self.assertEqual(employee['name'], self.valid_employee['name'])

    def test_create_multiple_employees_for_org(self):
        user_id = str(uuid.uuid4())
        self.valid_employee['auth_user_id'] = user_id
        code, employee = self.client.employee_create(self.org_id,
                                                     self.valid_employee)
        self.assertEqual(code, 201)
        self.assertEqual(employee['name'], self.valid_employee['name'])

        code, employee = self.client.employee_create(self.org_id,
                                                     self.valid_employee)
        self.assertEqual(code, 409)
        self.assertEqual(
            employee['error']['reason'],
            'Employee for user {} already exists in organization {}'.format(
                user_id, self.org_id
            ))

    def test_create_missing_required(self):
        params = self.valid_employee.copy()
        del params['name']
        code, ret = self.client.employee_create(self.org_id, params)
        self.assertEqual(code, 400)
        self.assertEqual(ret['error']['reason'], 'name is not provided')

    def test_create_unexpected(self):
        params = self.valid_employee.copy()
        params['param'] = '1'
        code, ret = self.client.employee_create(self.org_id, params)
        self.assertEqual(code, 400)
        self.assertEqual(ret['error']['reason'],
                         "Unexpected parameters: ['param']")

    def test_patch(self):
        code, employee = self.client.employee_create(self.org_id,
                                                     self.valid_employee)
        self.assertEqual(code, 201)

        params = {'name': '11'}
        code, ret = self.client.employee_update(employee['id'], params)
        self.assertEqual(code, 200)
        employee.update(params)
        self.assertDictEqual(ret, employee)

    def test_employee_patch_bu(self):
        code, employee = self.client.employee_create(self.org_id,
                                                     self.valid_employee)
        self.assertEqual(code, 201)

        code, org2 = self.client.organization_create({'name': "org2"})
        self.assertEqual(code, 201)

        code, res = self.client.employee_update(employee['id'],
                                                {'organization_id': org2['id']})
        self.assertEqual(code, 400)
        self.assertEqual(res['error']['reason'],
                         'Parameter "organization_id" is immutable')

    def test_delete_auth_assignments(self):
        code, root_employee = self.client.employee_create(
            self.org_id, self.valid_employee)
        self.assertEqual(code, 201)
        code, employee = self.client.employee_create(
            self.org_id, self.valid_employee2)
        self.assertEqual(code, 201)
        patch('rest_api.rest_api_server.controllers.employee.EmployeeController.'
              'auth_client').start()
        patch('rest_api.rest_api_server.controllers.employee.EmployeeController.'
              'auth_client.user_roles_get',
              return_value=(
                  200, [{'user_id': root_employee['auth_user_id']}])).start()

        code, get_cloud_acc = self.client.employee_get(employee['id'])
        self.assertEqual(code, 200)
        self.assertDictEqual(get_cloud_acc, employee)
        with patch('rest_api.rest_api_server.controllers.employee.AuthClient.assignment_list',
                   return_value=(200, [{
                       'assignment_resource': self.org['pool_id'],
                       'assignment_id': employee['auth_user_id']
                   }])) as p_assignment:
            with patch('rest_api.rest_api_server.controllers.employee.AuthClient.assignment_delete',
                       return_value=(204, None)) as p_ass_del:
                code, _ = self.client.employee_delete(employee['id'])
                self.assertEqual(code, 204)

                code, _ = self.client.employee_delete(employee['id'])
                self.assertEqual(code, 404)

                self.assertEqual(p_ass_del.call_count, 1)

    @patch('optscale_client.auth_client.client_v2.Client.assignment_list')
    def test_delete_org_manager(self, p_assignment_list):
        code, employee = self.client.employee_create(
            self.org_id, self.valid_employee)
        self.assertEqual(code, 201)
        p_assignment_list.return_value = (200, [{
            'assignment_id': str(uuid.uuid4()),
            'role_name': 'Manager',
            'assignment_resource': self.org_id}])
        patch('rest_api.rest_api_server.controllers.employee.EmployeeController.'
              'auth_client').start()

        # not the last org manager
        code, employee2 = self.client.employee_create(
            self.org_id, self.valid_employee2)
        self.assertEqual(code, 201)
        patch('rest_api.rest_api_server.controllers.employee.EmployeeController.'
              'auth_client.user_roles_get',
              return_value=(200,
                            [{'user_id': employee['auth_user_id']},
                             {'user_id': employee2['auth_user_id']}])).start()
        self._mock_auth()
        code, resp = self.client.employee_delete(employee2['id'])
        self.assertEqual(code, 204)

        # the last org manager
        patch('rest_api.rest_api_server.controllers.employee.EmployeeController.'
              'auth_client.user_roles_get',
              return_value=(200,
                            [{'user_id': employee['auth_user_id']}])).start()
        patch('rest_api.rest_api_server.controllers.employee.EmployeeController.'
              'auth_client.assignment_list', return_value=(
                  200, [{'assignment_id': str(uuid.uuid4()),
                         'role_name': 'Manager',
                         'assignment_resource': self.org_id}])).start()
        self.client.secret = ''

        self._mock_auth_user(employee['auth_user_id'])
        code, resp = self.client.employee_delete(employee['id'])
        self.assertEqual(code, 403)
        self.assertEqual(resp['error']['reason'], Err.OE0497.value[0])

    def test_delete_invalid_new_owner_id(self):
        code, emp = self.client.employee_create(
            self.org_id, {'name': 'emp', 'auth_user_id': self.gen_id()})
        self.assertEqual(code, 201)

        code, deleted_emp = self.client.employee_create(
            self.org_id, {'name': 'emp', 'auth_user_id': self.gen_id()})
        self.assertEqual(code, 201)
        db = DBFactory(DBType.Test, None).db
        engine = db.engine
        session = BaseDB.session(engine)()
        session.query(Employee).filter(
            Employee.id == deleted_emp['id']).update({
                'deleted_at': opttime.utcnow_timestamp()})
        session.commit()

        invalid_owners = ['123', emp['id'], deleted_emp['id']]
        for owner in invalid_owners:
            code, resp = self.client.employee_delete(
                emp['id'], new_owner_id=owner)
            self.assertEqual(code, 400)
            self.assertEqual(resp['error']['error_code'], 'OE0217')

    @patch('optscale_client.auth_client.client_v2.Client.assignment_list')
    def test_delete_new_default_owner(self, p_assignment_list):
        patch('rest_api.rest_api_server.controllers.employee.EmployeeController.'
              'auth_client').start()
        code, root_emp = self.client.employee_create(
            self.org_id, {'name': 'root_emp', 'auth_user_id': self.gen_id()})
        self.assertEqual(code, 201)
        self.update_default_owner_for_pool(self.org['pool_id'], root_emp['id'])
        self._mock_auth()

        # new_owner_id defined
        code, emp = self.client.employee_create(
            self.org_id, {'name': 'emp', 'auth_user_id': self.gen_id()})
        self.update_default_owner_for_pool(self.org['pool_id'], emp['id'])
        p_assignment_list.return_value = (200, [{
            'assignment_id': str(uuid.uuid4()),
            'role_name': 'Manager',
            'assignment_resource': self.org['pool_id']}])
        patch('rest_api.rest_api_server.controllers.employee.EmployeeController.'
              'auth_client.assignment_list', return_value=(
                  200, [{'assignment_id': str(uuid.uuid4()),
                         'role_name': 'Manager',
                         'assignment_resource': self.org_id}])).start()
        code, resp = self.client.employee_delete(
            emp['id'], new_owner_id=root_emp['id'])
        self.assertEqual(code, 204)
        code, pool = self.client.pool_get(self.org['pool_id'])
        self.assertEqual(code, 200)
        self.assertEqual(pool['default_owner_id'], root_emp['id'])

        # new owner from user_id
        code, emp = self.client.employee_create(
            self.org_id, {'name': 'emp', 'auth_user_id': self.gen_id()})
        self.update_default_owner_for_pool(self.org['pool_id'], emp['id'])
        with self.switch_user(emp['auth_user_id']):
            patch('rest_api.rest_api_server.controllers.employee.EmployeeController.'
                  'auth_client.user_roles_get',
                  return_value=(200,
                                [{'user_id': emp['auth_user_id']},
                                 {'user_id': root_emp['auth_user_id']}])).start()
            code, resp = self.client.employee_delete(emp['id'])
            self.assertEqual(code, 204)
            code, pool = self.client.pool_get(self.org['pool_id'])
            self.assertEqual(code, 200)
            self.assertEqual(pool['default_owner_id'], root_emp['id'])

        # new owner from organization managers
        code, emp = self.client.employee_create(
            self.org_id, {'name': 'emp', 'auth_user_id': self.gen_id()})
        self.update_default_owner_for_pool(self.org['pool_id'], emp['id'])
        self.client.token = ''
        code, resp = self.client.employee_delete(emp['id'])
        self.assertEqual(code, 204)
        code, pool = self.client.pool_get(self.org['pool_id'])
        self.assertEqual(code, 200)
        self.assertEqual(pool['default_owner_id'], root_emp['id'])

    @patch('optscale_client.auth_client.client_v2.Client.assignment_list')
    def test_delete_change_default_owner(self, p_assignment_list):
        patch('rest_api.rest_api_server.controllers.employee.EmployeeController.'
              'auth_client').start()
        pools = self.prepare_pools_hierarchy()
        code, root_emp = self.client.employee_create(
            self.org_id, {'name': 'root_emp', 'auth_user_id': self.gen_id()})
        self.assertEqual(code, 201)
        self.update_default_owner_for_pool(self.org['pool_id'], root_emp['id'])
        for name, pool_id in pools.items():
            self.update_default_owner_for_pool(pool_id, root_emp['id'])
        self._mock_auth()

        # one sub pool
        code, emp_p1 = self.client.employee_create(
            self.org_id, {'name': 'emp_p1', 'auth_user_id': self.gen_id()})
        self.assertEqual(code, 201)
        self.update_default_owner_for_pool(pools['p1'], emp_p1['id'])
        p_assignment_list.return_value = (200, [{
            'assignment_id': str(uuid.uuid4()),
            'role_name': 'Manager',
            'assignment_resource': pools['p1']}])
        patch('rest_api.rest_api_server.controllers.employee.EmployeeController.'
              'auth_client.user_roles_get',
              return_value=(200,
                            [{'user_id': emp_p1['auth_user_id']},
                             {'user_id': root_emp['auth_user_id']}])).start()
        code, resp = self.client.employee_delete(emp_p1['id'])
        self.assertEqual(code, 204)
        code, pool = self.client.pool_get(pools['p1'])
        self.assertEqual(pool['default_owner_id'], root_emp['id'])

        # sub pool and parent sub pool
        code, emp_p1_p11 = self.client.employee_create(
            self.org_id, {'name': 'emp_p1_p11', 'auth_user_id': self.gen_id()})
        self.assertEqual(code, 201)
        self.update_default_owner_for_pool(pools['p1'], emp_p1_p11['id'])
        self.update_default_owner_for_pool(pools['p11'], emp_p1_p11['id'])

        code, resp = self.client.employee_delete(emp_p1_p11['id'])
        self.assertEqual(code, 204)
        code, pool = self.client.pool_get(pools['p1'])
        self.assertEqual(pool['default_owner_id'], root_emp['id'])
        code, pool = self.client.pool_get(pools['p11'])
        self.assertEqual(pool['default_owner_id'], root_emp['id'])

    @patch('optscale_client.auth_client.client_v2.Client.assignment_list')
    def test_delete_reassign_rules(self, p_assignment_list):
        code, root_emp = self.client.employee_create(
            self.org_id, {'name': 'root_emp', 'auth_user_id': self.user_id})
        self.assertEqual(code, 201)
        code, emp = self.client.employee_create(
            self.org_id, {'name': 'emp', 'auth_user_id': self.gen_id()})
        self.assertEqual(code, 201)
        pools = self.prepare_pools_hierarchy()
        self.update_default_owner_for_pool(self.org['pool_id'], root_emp['id'])
        for name, pool_id in pools.items():
            self.update_default_owner_for_pool(pool_id, emp['id'])
        self._mock_auth()
        rules = []
        conditions = [{"type": "name_starts_with",
                       "meta_info": "name_starts_with"}]
        for pool_name, pool_id in pools.items():
            code, rule = self.client.rules_create(
                self.org_id, {'name': pool_name, "conditions": conditions,
                              'pool_id': pool_id, 'owner_id': emp['id']})
            self.assertEqual(code, 201)
            rules.append(rule['id'])
        patch('rest_api.rest_api_server.controllers.employee.EmployeeController.'
              'auth_client').start()
        p_assignment_list.return_value = (200, [])
        patch('rest_api.rest_api_server.controllers.employee.EmployeeController.'
              'auth_client.user_roles_get',
              return_value=(200,
                            [{'user_id': emp['auth_user_id']},
                             {'user_id': root_emp['auth_user_id']}])).start()
        code, resp = self.client.employee_delete(emp['id'])
        self.assertEqual(code, 204)
        for rule_id in rules:
            code, rule = self.client.rule_get(rule_id)
            self.assertEqual(rule['owner_id'], root_emp['id'])

    @patch('optscale_client.auth_client.client_v2.Client.assignment_list')
    def test_delete_reassign_bookings(self, p_assignment_list):
        patch('rest_api.rest_api_server.controllers.employee.'
              'EmployeeController.auth_client').start()
        patch('rest_api.rest_api_server.controllers.shareable_resource.'
              'ShareableBookingController.publish_task').start()
        patch('rest_api.rest_api_server.controllers.shareable_resource.'
              'ShareableBookingController.check_employee_permission').start()
        code, root_emp = self.client.employee_create(
            self.org_id, {'name': 'root_emp', 'auth_user_id': self.user_id})
        self.assertEqual(code, 201)
        code, emp = self.client.employee_create(
            self.org_id, {'name': 'emp', 'auth_user_id': self.gen_id()})
        self.assertEqual(code, 201)
        self.update_default_owner_for_pool(self.org['pool_id'], root_emp['id'])
        cloud_acc_config = {
            'name': 'my cloud_acc',
            'type': 'aws_cnr',
            'config': {
                'access_key_id': 'key',
                'secret_access_key': 'secret',
                'config_scheme': 'create_report'
            }
        }
        patch('rest_api.rest_api_server.controllers.cloud_account.'
              'CloudAccountController._configure_report').start()
        _, cloud_acc = self.create_cloud_account(self.org_id, cloud_acc_config,
                                                 auth_user_id=self.user_id)
        self.assertEqual(code, 201)
        code, resource = self.cloud_resource_create(
            cloud_acc['id'], {
                'cloud_resource_id': self.gen_id(),
                'name': 'name',
                'resource_type': 'Instance',
                'employee_id': emp['id'],
                'pool_id': self.org['pool_id'],
                'active': True
            })
        self.assertEqual(code, 201)
        self.resources_collection.update_one(
            filter={
                '_id': resource['id']
            },
            update={'$set': {
                'shareable': True}}
        )
        p_assignment_list.return_value = (200, [])
        self._mock_auth()
        patch('optscale_client.auth_client.client_v2.Client.user_roles_get',
              return_value=(200,
                            [{'user_id': emp['auth_user_id']},
                             {'user_id': root_emp['auth_user_id']}])).start()
        patch('rest_api.rest_api_server.controllers.employee.'
              'EmployeeController.auth_client.user_roles_get',
              return_value=(200,
                            [{'user_id': emp['auth_user_id']},
                             {'user_id': root_emp['auth_user_id']}])).start()
        schedule_book = {
            'resource_id': resource['id'],
            'acquired_by_id': emp['id'],
        }
        code, booking = self.client.shareable_book_create(
            self.org_id, schedule_book)
        self.assertEqual(code, 201)

        code, resp = self.client.employee_delete(emp['id'])
        self.assertEqual(code, 204)
        code, res = self.client.shareable_book_get(booking['id'])
        self.assertEqual(code, 200)
        self.assertEqual(res['acquired_by']['id'], root_emp['id'])

    @patch('optscale_client.auth_client.client_v2.Client.assignment_list')
    def test_delete_reassign_resources(self, p_assignment_list):
        patch('rest_api.rest_api_server.controllers.employee.EmployeeController.'
              'auth_client').start()
        code, root_emp = self.client.employee_create(
            self.org_id, {'name': 'root_emp', 'auth_user_id': self.user_id})
        self.assertEqual(code, 201)
        code, emp = self.client.employee_create(
            self.org_id, {'name': 'emp', 'auth_user_id': self.gen_id()})
        self.assertEqual(code, 201)
        self.update_default_owner_for_pool(self.org['pool_id'], root_emp['id'])
        cloud_acc_config = {
            'name': 'my cloud_acc',
            'type': 'aws_cnr',
            'config': {
                'access_key_id': 'key',
                'secret_access_key': 'secret',
                'config_scheme': 'create_report'
            }
        }
        patch('rest_api.rest_api_server.controllers.cloud_account.'
              'CloudAccountController._configure_report').start()
        _, cloud_acc = self.create_cloud_account(self.org_id, cloud_acc_config,
                                                 auth_user_id=self.user_id)
        self.assertEqual(code, 201)
        code, resource = self.cloud_resource_create(
            cloud_acc['id'], {
                'cloud_resource_id': self.gen_id(),
                'name': 'name',
                'resource_type': 'type',
                'employee_id': emp['id'],
                'pool_id': self.org['pool_id']
            })
        self.assertEqual(code, 201)
        self.expenses.append({
            'cost': 11,
            'cloud_account_id': cloud_acc['id'],
            'resource_id': resource['id'],
            'date': opttime.utcnow() - timedelta(days=10),
            'sign': 1
        })
        p_assignment_list.return_value = (200, [])
        self._mock_auth()
        patch('rest_api.rest_api_server.controllers.employee.EmployeeController.'
              'auth_client.user_roles_get',
              return_value=(200,
                            [{'user_id': emp['auth_user_id']},
                             {'user_id': root_emp['auth_user_id']}])).start()

        code, resp = self.client.employee_delete(emp['id'])
        self.assertEqual(code, 204)
        code, res = self.client.cloud_resource_get(resource['id'])
        self.assertEqual(code, 200)
        self.assertEqual(res['employee_id'], root_emp['id'])

    def test_delete_assignment_requests(self):
        patch('rest_api.rest_api_server.controllers.employee.EmployeeController.'
              'auth_client').start()
        code, root_emp = self.client.employee_create(
            self.org_id, {'name': 'root_emp', 'auth_user_id': self.user_id})
        self.assertEqual(code, 201)
        code, emp = self.client.employee_create(
            self.org_id, {'name': 'emp', 'auth_user_id': self.gen_id()})
        self.assertEqual(code, 201)
        self.update_default_owner_for_pool(self.org['pool_id'], root_emp['id'])
        cloud_acc_config = {
            'name': 'my cloud_acc',
            'type': 'aws_cnr',
            'config': {
                'access_key_id': 'key',
                'secret_access_key': 'secret',
                'config_scheme': 'create_report'
            }
        }
        patch('rest_api.rest_api_server.controllers.cloud_account.'
              'CloudAccountController._configure_report').start()
        _, cloud_acc = self.create_cloud_account(self.org_id, cloud_acc_config,
                                                 auth_user_id=self.user_id)
        self.assertEqual(code, 201)
        code, resource_out = self.cloud_resource_create(
            cloud_acc['id'], {
                'cloud_resource_id': self.gen_id(),
                'name': 'name',
                'resource_type': 'type',
                'employee_id': emp['id'],
                'pool_id': self.org['pool_id']
            })
        self.assertEqual(code, 201)
        code, resource_in = self.cloud_resource_create(
            cloud_acc['id'], {
                'cloud_resource_id': self.gen_id(),
                'name': 'name',
                'resource_type': 'type',
                'employee_id': root_emp['id'],
                'pool_id': self.org['pool_id']
            })
        self.assertEqual(code, 201)
        request_in = {
            'resource_id': resource_in['id'],
            'message': 'inc',
            'approver_id': emp['id']}
        code, req_in = self.client.assignment_request_create(self.org_id,
                                                             request_in)
        self.assertEqual(code, 201)

        with self.switch_user(emp['auth_user_id']):
            request_out = {
                'resource_id': resource_out['id'],
                'message': 'inc',
                'approver_id': root_emp['id']}
            code, req_in = self.client.assignment_request_create(self.org_id,
                                                                 request_out)
            self.assertEqual(code, 201)
            code, resp = self.client.assignment_request_list(self.org_id)
            self.assertEqual(code, 200)
            code, resp = self.client.layouts_create(self.org_id, {
                'type': 'test',
                'name': 'test',
                'shared': True
            })
            self.assertEqual(code, 201)
        patch('rest_api.rest_api_server.controllers.employee.EmployeeController.'
              'auth_client.user_roles_get',
              return_value=(200,
                            [{'user_id': emp['auth_user_id']},
                             {'user_id': root_emp['auth_user_id']}])).start()
        code, resp = self.client.employee_delete(emp['id'])
        self.assertEqual(code, 204)
        code, resp = self.client.assignment_request_list(self.org_id)
        self.assertEqual(code, 200)
        self.assertEqual(
            resp['assignment_requests']['incoming'][0]['approver_id'],
            root_emp['id'])
        self.assertEqual(
            resp['assignment_requests']['outgoing'][0]['requester_id'],
            root_emp['id'])
        code, resp = self.client.layouts_list(self.org_id)
        self.assertEqual(code, 200)
        self.assertEqual(resp['layouts'][0]['owner_id'], root_emp['id'])

    def test_delete_self_deleting(self):
        patch('rest_api.rest_api_server.controllers.employee.EmployeeController.'
              'auth_client').start()
        code, employee = self.client.employee_create(self.org_id,
                                                     self.valid_employee)
        self.assertEqual(code, 201)
        patch('rest_api.rest_api_server.controllers.employee.EmployeeController.'
              'auth_client.assignment_list', return_value=(
                  200, [{'assignment_id': str(uuid.uuid4()),
                         'role_name': 'Manager',
                         'assignment_resource': self.org_id}])).start()
        code, resp = self.client.employee_delete(employee['id'],
                                                 new_owner_id=employee['id'])
        self.assertEqual(code, 400)
        self.assertEqual(resp['error']['error_code'], 'OE0217')

        # new_owner_id not defined, but user token provided
        self._mock_auth_user(employee['auth_user_id'])
        code, employee2 = self.client.employee_create(self.org_id,
                                                      self.valid_employee2)
        self.assertEqual(code, 201)
        patch('rest_api.rest_api_server.controllers.employee.EmployeeController.'
              'auth_client.user_roles_get',
              return_value=(
                  200, [{'user_id': employee['auth_user_id']},
                        {'user_id': employee2['auth_user_id']}])).start()
        code, resp = self.client.employee_delete(employee['id'])
        self.assertEqual(code, 204)

    def test_list(self):
        code, employee = self.client.employee_create(self.org_id,
                                                     self.valid_employee)
        self.assertEqual(code, 201)
        code, employee_list = self.client.employee_list(self.org_id)
        self.assertEqual(code, 200)
        # default employee + created employee
        self.assertEqual(len(employee_list['employees']), 2)

        self.valid_employee.update({'auth_user_id': self.gen_id()})

        code, employee2 = self.client.employee_create(self.org_id,
                                                      self.valid_employee)
        self.assertEqual(code, 201)

        code, organization2 = self.client.organization_create(
            {'name': "organization2"})
        self.assertEqual(code, 201)
        code, employee3 = self.client.employee_create(organization2['id'],
                                                      self.valid_employee)
        self.assertEqual(code, 201)

        self.p_auth_users.stop()
        patch_auth_users = patch(
            'rest_api.rest_api_server.controllers.employee.EmployeeController._get_auth_users',
            return_value=[
                {
                    'id': employee['auth_user_id'],
                    'slack_connected': False,
                    'jira_connected': True
                },
                {
                    'id': employee2['auth_user_id'],
                    'slack_connected': True,
                    'jira_connected': False
                }
            ]
        ).start()
        code, employee_list = self.client.employee_list(self.org_id)
        patch_auth_users.stop()
        slack_count = sum(employee['slack_connected']
                          for employee in employee_list['employees'])
        jira_count = sum(employee['jira_connected']
                         for employee in employee_list['employees'])
        self.assertEqual(code, 200)
        self.assertEqual(len(employee_list['employees']), 3)
        self.assertEqual(slack_count, 1)
        self.assertEqual(jira_count, 1)

        valid_fields = ['id', 'name', 'deleted_at', 'created_at',
                        'organization_id', 'auth_user_id', 'slack_connected']
        code, employee_list = self.client.employee_list(self.org_id)
        self.assertEqual(code, 200)
        for field in valid_fields:
            self.assertIn(field, employee_list['employees'][0])
        requested_fields = ['id']
        for field in valid_fields:
            requested_fields.append(field)
            code, employee_list = self.client.employee_list(
                self.org_id, fields=requested_fields)
            self.assertEqual(code, 200)
            for key in employee_list['employees'][0].keys():
                self.assertIn(key, requested_fields)
            del requested_fields[0]

    @patch(
        'rest_api.rest_api_server.controllers.employee.EmployeeController._get_roles_info')
    def test_list_with_roles(self, p_roles):
        auth_user_id = str(uuid.uuid4())
        employee = self.valid_employee
        employee['auth_user_id'] = auth_user_id
        assignment_res_id = self.org_id
        p_roles.return_value = [
            {'user_email': 'jmoth@example.corp',
             'role_name': 'Manager', 'user_display_name': 'John Smith',
             'assignment_type_id': 1, 'user_id': '%s' % auth_user_id,
             'role_id': 3,
             'assignment_id': '7e57ef93-5f92-4bb1-a999-3f17b5fc72f4',
             'role_purpose': 'optscale_manager', 'role_scope_id': None,
             'assignment_resource_id': '%s' % self.org_id}]
        code, employee = self.client.employee_create(self.org_id,
                                                     employee)
        self.assertEqual(code, 201)
        code, employee_list = self.client.employee_list(self.org_id,
                                                        roles=True)
        self.assertEqual(code, 200)
        # default employee + created employee
        self.assertEqual(len(employee_list['employees']), 2)
        employee = [x for x in employee_list['employees'] if x[
            'id'] == employee['id']][0]
        self.assertIsNotNone(employee['user_email'])
        self.assertIsNotNone(employee['user_display_name'])
        self.assertEqual(len(employee['assignments']), 1)
        assignment = employee['assignments'][0]
        self.assertEqual(
            assignment['assignment_resource_id'], assignment_res_id)
        self.assertEqual(
            assignment['assignment_resource_name'], self.org['name'])
        self.assertEqual(
            assignment['assignment_resource_purpose'], 'business_unit')

    @patch(
        'rest_api.rest_api_server.controllers.employee.EmployeeController._get_roles_info')
    def test_list_with_excluded_roles(self, p_roles):
        auth_user_id = str(uuid.uuid4())
        employee = self.valid_employee
        employee['auth_user_id'] = auth_user_id
        base_user_role = {
            'user_email': 'jmoth@example.corp', 'user_display_name': 'John Smith',
            'assignment_type_id': 1, 'user_id': '%s' % auth_user_id,
            'role_id': 3, 'assignment_id': '7e57ef93-5f92-4bb1-a999-3f17b5fc72f4',
            'role_scope_id': None, 'assignment_resource_id': '%s' % self.org_id
        }
        base_user_role.update({'role_name': 'Member',
                               'role_purpose': 'optscale_member'})
        member_user_role = base_user_role.copy()
        base_user_role.update({'role_name': 'Manager',
                               'role_purpose': 'optscale_manager'})
        manager_user_role = base_user_role.copy()
        p_roles.return_value = [member_user_role, manager_user_role]
        code, employee = self.client.employee_create(self.org_id,
                                                     employee)
        self.assertEqual(code, 201)
        code, employee_list = self.client.employee_list(self.org_id,
                                                        roles=True)
        self.assertEqual(code, 200)
        # exclude default employee
        employees = [x for x in employee_list['employees'] if
                     x['auth_user_id'] == auth_user_id]
        self.assertEqual(len(employees), 1)
        employee = employees[0]
        self.assertEqual(len(employee['assignments']), 1)
        manager_employee_assignment = employee['assignments'][0]
        self.assertEqual(manager_employee_assignment['role_name'], 'Manager')
        self.assertEqual(manager_employee_assignment['purpose'], 'optscale_manager')

    def test_list_current_only(self):
        users = []
        employees = []
        # create 3 employees
        for _ in range(3):
            user_id = self.gen_id()
            users.append(user_id)
            employee = self.valid_employee
            employee.update({'auth_user_id': user_id})
            code, employee = self.client.employee_create(self.org_id,
                                                         employee)
            self.assertEqual(code, 201)
            employees.append(employee)

        # check employee list shows all 3 users
        code, employee_list = self.client.employee_list(self.org_id)
        self.assertEqual(code, 200)
        # default + 3 created
        self.assertEqual(len(employee_list['employees']), 4)

        # for every user check response with current_only=True
        for i in range(3):
            current_user = users[i]
            self._mock_auth_user(current_user)
            code, employee_list = self.client.employee_list(
                self.org_id, current_only=True)
            self.assertEqual(code, 200)
            self.assertEqual(len(employee_list['employees']), 1)
            self.assertEqual(employee_list['employees'][0]['id'],
                             employees[i]['id'])

        # create another org and check current_only=True returns empty list
        _, org2 = self.client.organization_create(
            {'name': "organization2"})
        self._mock_auth_user(users[0])
        code, employee_list = self.client.employee_list(
            org2['id'], current_only=True)
        self.assertEqual(code, 200)
        self.assertEqual(len(employee_list['employees']), 0)

    def test_list_exclude_myself(self):
        auth_user_id = self.gen_id()
        valid_employee = self.valid_employee
        valid_employee.update({'auth_user_id': auth_user_id})
        code, employee = self.client.employee_create(self.org_id,
                                                     valid_employee)
        self.assertEqual(code, 201)
        self._mock_auth_user(auth_user_id)
        code, employee_list = self.client.employee_list(self.org_id,
                                                        exclude_myself=True)
        self.assertEqual(code, 200)
        # default employee
        self.assertEqual(len(employee_list['employees']), 1)

        code, employee_list = self.client.employee_list(self.org_id)
        self.assertEqual(code, 200)
        # default employee + myself
        self.assertEqual(len(employee_list['employees']), 2)

    def test_get_with_invalid_params(self):
        code, employee = self.client.employee_create(self.org_id,
                                                     self.valid_employee)
        self.assertEqual(code, 201)
        _, response = self.client.employee_list(self.org_id, current_only=True,
                                                exclude_myself=True)
        self.assertEqual(response['error']['error_code'], 'OE0427')

    def test_authorized_employees_params_validation(self):
        valid_params = {
            'object_type': 'cloud_resource',
            'object_id': str(uuid.uuid4()),
            'permissions': ['MANAGE_RESOURCES']
        }
        for p in valid_params.keys():
            params = valid_params.copy()

            params[p] = None
            code, resp = self.client.authorized_employee_list(
                self.org_id, **params)
            self.assertEqual(code, 400)
            self.verify_error_code(resp, 'OE0216')

            params[p] = ''
            code, resp = self.client.authorized_employee_list(
                self.org_id, **params)
            self.assertEqual(code, 400)
            self.verify_error_code(resp, 'OE0215')

    def test_authorized_employees(self):
        patch('rest_api.rest_api_server.controllers.employee.EmployeeController.'
              'auth_client').start()
        patch(
            'rest_api.rest_api_server.controllers.employee.EmployeeController.'
            'auth_client.authorize_user_list', return_value=(200, {
                self._user_id: [],
                self.user_id: ['MANAGE_RESOURCES'],
                self.user_id2: ['MANAGE_RESOURCES', 'MANAGE_POOLS'],
            })).start()
        valid_params = {
            'object_type': 'cloud_resource',
            'object_id': str(uuid.uuid4()),
            'permissions': ['MANAGE_RESOURCES', 'MANAGE_POOLS']
        }
        self.client.employee_create(self.org_id, self.valid_employee)
        self.client.employee_create(self.org_id, self.valid_employee2)
        code, resp = self.client.authorized_employee_list(
            self.org_id, **valid_params)
        self.assertEqual(code, 200)
        employees = resp['employees']
        self.assertEqual(len(employees), 1)
        self.assertEqual(employees[0]['auth_user_id'], self.user_id2)

    @patch('optscale_client.auth_client.client_v2.Client.assignment_list')
    def test_delete_org_constraint_on_employee_deleting(self, p_assignment_list):
        patch('rest_api.rest_api_server.controllers.employee.EmployeeController.'
              'auth_client').start()
        code, root_emp = self.client.employee_create(
            self.org_id, {'name': 'root_emp', 'auth_user_id': self.user_id})
        self.assertEqual(code, 201)
        code, emp = self.client.employee_create(
            self.org_id, {'name': 'emp', 'auth_user_id': self.gen_id()})
        self.assertEqual(code, 201)
        self.update_default_owner_for_pool(self.org['pool_id'], root_emp['id'])
        p_assignment_list.return_value = (200, [])
        self._mock_auth()
        patch('rest_api.rest_api_server.controllers.employee.EmployeeController.'
              'auth_client.user_roles_get',
              return_value=(200,
                            [{'user_id': emp['auth_user_id']},
                             {'user_id': root_emp['auth_user_id']}])).start()

        constr = self.create_org_constraint(self.org_id, self.org['pool_id'],
                                            filters={'owner_id': [emp['id']]})

        code, resp = self.client.employee_delete(emp['id'])
        self.assertEqual(code, 204)
        code, resp = self.client.organization_constraint_get(constr['id'])
        self.assertEqual(code, 404)
        self.assertEqual(resp['error']['error_code'], 'OE0002')

    @patch('optscale_client.auth_client.client_v2.Client.assignment_list')
    def test_delete_org_hits_on_employee_deleting(self, p_assignment_list):
        patch('rest_api.rest_api_server.controllers.employee.EmployeeController.'
              'auth_client').start()
        code, root_emp = self.client.employee_create(
            self.org_id, {'name': 'root_emp', 'auth_user_id': self.user_id})
        self.assertEqual(code, 201)
        code, emp = self.client.employee_create(
            self.org_id, {'name': 'emp', 'auth_user_id': self.gen_id()})
        self.assertEqual(code, 201)
        self.update_default_owner_for_pool(self.org['pool_id'], root_emp['id'])
        p_assignment_list.return_value = (200, [])
        self._mock_auth()
        patch('rest_api.rest_api_server.controllers.employee.EmployeeController.'
              'auth_client.user_roles_get',
              return_value=(200,
                            [{'user_id': emp['auth_user_id']},
                             {'user_id': root_emp['auth_user_id']}])).start()

        self.create_org_limit_hit(self.org_id, self.org['pool_id'],
                                  filters={'owner_id': [emp['id']]})

        code, resp = self.client.employee_delete(emp['id'])
        self.assertEqual(code, 204)
        db = DBFactory(DBType.Test, None).db
        engine = db.engine
        session = BaseDB.session(engine)()
        hits = session.query(OrganizationLimitHit).filter(and_(
            OrganizationLimitHit.organization_id == self.org_id,
            OrganizationLimitHit.deleted.is_(False)
        )).one_or_none()
        self.assertEqual(hits, None)

    def test_employees_last_login(self):
        code, employee = self.client.employee_create(self.org_id,
                                                     self.valid_employee)
        self.assertEqual(code, 201)

        self.valid_employee.update({'auth_user_id': self.gen_id()})
        code, employee2 = self.client.employee_create(self.org_id,
                                                      self.valid_employee)
        self.assertEqual(code, 201)

        self.p_auth_users.stop()
        patch_auth_users = patch(
            'rest_api.rest_api_server.controllers.employee.EmployeeController._get_auth_users',
            return_value=[
                {
                    'id': employee['auth_user_id'],
                    'slack_connected': True,
                    'jira_connected': False,
                    'last_login': int(datetime(2022, 1, 1).timestamp())
                },
                {
                    'id': employee2['auth_user_id'],
                    'slack_connected': True,
                    'jira_connected': False,
                    'last_login': int(datetime(2022, 1, 2).timestamp())
                }
            ]
        ).start()
        code, employee_list = self.client.employee_list(self.org_id)
        patch_auth_users.stop()

        self.assertEqual(code, 200)
        self.assertEqual(len(employee_list['employees']), 3)
        for e in employee_list['employees']:
            self.assertIsNotNone(e['last_login'])

        patch('rest_api.rest_api_server.handlers.v1.base.BaseAuthHandler.'
              'check_cluster_secret', return_value=False).start()
        _, employee_list = self.client.employee_list(self.org_id)
        for e in employee_list['employees']:
            self.assertIsNotNone(e['last_login'])

        def side_eff(action, *args, **kwargs):
            if action == 'EDIT_PARTNER':
                raise OptHTTPError(403, Err.OE0234, [])
            else:
                return {}
        patch(
            'rest_api.rest_api_server.handlers.v1.base.'
            'BaseAuthHandler.check_permissions',
            side_effect=side_eff).start()
        _, employee_list = self.client.employee_list(self.org_id)
        for e in employee_list['employees']:
            self.assertIsNone(e.get('last_login'))
